﻿using System;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAccess;
using EPiServer.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PageTypeBuilder;
using TemplateFoundation.Configuration;
using TemplateFoundation.Core;
using TemplateFoundation.Feeds;
using TemplateFoundation.PageTypes;
using TemplateFoundation.Tests.Environment;
using TemplateFoundation.Tests.PageTypes;

namespace TemplateFoundation.Tests.UnitTests.TemplateFoundation.Feeds
{
    [TestClass]
    public class RssFeedTest
    {
        private RssChannel CreateRssChannel(string languageBranch)
        {
           return new RssChannel("RSS test feed", "http://tempuri.org", "This is a test RSS feed")
            {
                Language = languageBranch
            };
        }

        [TestMethod]
        public void CreateRssFeedItemWithoutFullContent()
        {
            const string description = "This is the description.";

            var page = (StandardPage)PageStructure.CreateStandardPages("RSS feed item page", PageReference.StartPage, 1).First();

            page.Description = description;
            
            var item = new RssItem(page, false);

            Assert.IsTrue(item.Description==description, "Description differed from page's Description property");
        }

        [TestMethod]
        public void CreateRssFeedItemWithFullContent()
        {
            const string description = "This is the description.";
            const string mainBody = "<p>This is the main body.</p>";
            const string expectedMainBody = "<![CDATA[ <p>This is the main body.</p> ]]>";

            var page = (StandardPage)PageStructure.CreateStandardPages("RSS feed item page", PageReference.StartPage, 1).First();

            page.Description = description;
            page.MainBody = mainBody;

            var item = new RssItem(page, true);

            Assert.IsTrue(item.Description==expectedMainBody, "Description differed incorrectly from page's MainBody property");
        }

        [TestMethod]
        public void EnsureImagesAreEmbeddedWithAbsoluteUrls()
        {
            var siteUrl = EPiServer.Configuration.Settings.Instance.SiteUrl.ToString();

            if(siteUrl.EndsWith("/"))
            {
                siteUrl = siteUrl.Substring(0, siteUrl.Length - 1);
            }

            const string mainBody = "<p>Absolute image URL: <img src=\"http://tedgustaf.com/image.png\" /> Here are images with virtual links: <img src=\"/MyImage.jpg\" /> <img src='/MyImage.jpg' /> <img src=/MyImage.jpg /></p>";
            string expectedMainBody = string.Format("<![CDATA[ <p>Absolute image URL: <img src=\"http://tedgustaf.com/image.png\" /> Here are images with virtual links: <img src=\"{0}/MyImage.jpg\" /> <img src=\"{0}/MyImage.jpg\" /> <img src=\"{0}/MyImage.jpg\" /></p> ]]>", siteUrl);

            var page = (StandardPage)PageStructure.CreateStandardPages("RSS feed item page", PageReference.StartPage, 1).First();

            page.MainBody = mainBody;

            var item = new RssItem(page, true);

            Assert.IsTrue(item.Description==expectedMainBody, "Image URLs were not converted to absolute");
        }

        [TestMethod]
        public void EnsureLinksAreEmbeddedWithAbsoluteUrls()
        {
            var siteUrl = EPiServer.Configuration.Settings.Instance.SiteUrl.ToString();

            if (siteUrl.EndsWith("/"))
            {
                siteUrl = siteUrl.Substring(0, siteUrl.Length - 1);
            }

            const string mainBody = "<p>Absolute link: <a href=\"http://www.tedgustaf.com\" /> Here are virtual links: <a href=\"/some/page.html\" /> <a href='/some/other/page.html' /> <a href=/some/third/page.html /></p>";
            string expectedMainBody = string.Format("<![CDATA[ <p>Absolute link: <a href=\"http://www.tedgustaf.com\" /> Here are virtual links: <a href=\"{0}/some/page.html\" /> <a href=\"{0}/some/other/page.html\" /> <a href=\"{0}/some/third/page.html\" /></p> ]]>", siteUrl);

            var page = (StandardPage)PageStructure.CreateStandardPages("RSS feed item page", PageReference.StartPage, 1).First();

            page.MainBody = mainBody;

            var item = new RssItem(page, true);

            Assert.IsTrue(item.Description==expectedMainBody, "Links were not converted to absolute");
        }

        [TestMethod]
        public void EnsureRssItemFallsBackToDescriptionWhenNotImplementingIStandardPage()
        {
            var page = (PageTypeBase)PageStructure.CreatePersonPage("John Doe").CreateWritableClone();

            page.Description = "description here";

            var item = new RssItem(page, true); // Full content, but should fallback to description

            Assert.IsTrue(item.Description=="description here", "RSS item did not fall back to page description when page did not inherit IStandardPage");
        }

        /// <summary>
        /// Creates an RSS feed based on EPiServer pages
        /// </summary>
        [TestMethod]
        public void TestCreateRssFeed()
        {
            // Create a page collection
            var pages = new PageDataCollection();
            for(int i=0;i<100;i++)
            {
                pages.Add(new RandomValuesPageData());
            }

            var channel = CreateRssChannel(pages[0].LanguageBranch);

            foreach (var page in pages.Cast<RandomValuesPageData>())
            {
                var rssItem = new RssItem(page);

                // Assert property values
                Assert.IsFalse(string.IsNullOrEmpty(rssItem.Title), "The RSS item's title was empty");
                Assert.IsFalse(string.IsNullOrEmpty(rssItem.Link), "The RSS item's link was empty");
                Assert.AreEqual(rssItem.Author, page.Author, "Author element in RSS item differs from page's Author property");
                Assert.AreEqual(rssItem.Comments, string.Empty, "Comments string wasn't empty, comments aren't supported");
                Assert.AreEqual(rssItem.Date, page.StartPublish, "The date differs from the StartPublish property");
                Assert.AreEqual(rssItem.Description, page.Description, "The description differs from the Description property");
                Assert.AreEqual(rssItem.Guid, page.PageGuid.ToString(), "The GUID doesn't match the page GUID");
                Assert.AreEqual(rssItem.Link, page.GetFriendlyUrl(true), "The link doesn't match the page's friendly URL");
                Assert.AreEqual(rssItem.Title, page.Title ?? page.PageName, "The title differs from the page's Title property");
                Assert.AreEqual(rssItem.Language, page.LanguageBranch, "Language differes from page language");

                // Assert page tags appear as RSS item categories
                for (int i=0; i<rssItem.Categories.Count(); i++)
                {
                    Assert.AreEqual(rssItem.Categories[i], page.Tags.ToArray()[i].Name, rssItem.Categories[i] + " wasn't found in the page's tag collection, or is in the wrong position");
                }

                // Assert items with a language other than the channel language cannot be added
                if(page.LanguageBranch!=channel.Language)
                {
                    try
                    {
                        channel.AddItem(rssItem);

                        Assert.Fail("Was able to add RSS item in a language other than that of the feed");
                    }
                    catch
                    {
                        // AddItem() should fail if the languages differ
                    }
                }
                else
                {
                    channel.AddItem(rssItem);
                }
            }

            // Verify sort order
            for (int i=0; i<channel.Items.Count;i++)
            {
                if(i>0)
                {
                    Assert.IsTrue(channel.Items.ElementAt(i).Date<=channel.Items.ElementAt(i-1).Date,"The RSS feed is not sorted in reverse date order");
                }
            }

            // Verify only pages with the same language as the channel appear in the RSS feed
            Assert.AreEqual(
                channel.Items.Count,
                pages.Where(p => p.LanguageBranch == channel.Language).Count(),
                "Number of items in feed doesn't match number of pages with the same language as the feed");
        }
    }
}
